import datetime

from django.db import models
from django.utils.timesince import timesince
from django.utils.translation import gettext_lazy as _

from accounts.models import Account
from common.base import BaseModel
from common.models import Address, Org, Profile, Teams, User
from common.utils import CURRENCY_CODES


class Invoice(BaseModel):
    """Model definition for Invoice."""

    INVOICE_STATUS = (
        ("Draft", "Draft"),
        ("Sent", "Sent"),
        ("Paid", "Paid"),
        ("Pending", "Pending"),
        ("Cancelled", "Cancel"),
    )

    invoice_title = models.CharField(_("Invoice Title"), max_length=50)
    invoice_number = models.CharField(_("Invoice Number"), max_length=50)
    from_address = models.ForeignKey(
        Address,
        related_name="invoice_from_address",
        on_delete=models.SET_NULL,
        null=True,
    )
    to_address = models.ForeignKey(
        Address, related_name="invoice_to_address", on_delete=models.SET_NULL, null=True
    )
    name = models.CharField(_("Name"), max_length=100)
    email = models.EmailField(_("Email"))
    assigned_to = models.ManyToManyField(Profile, related_name="invoice_assigned_to")
    # quantity is the number of hours worked
    quantity = models.PositiveIntegerField(default=0)
    # rate is the rate charged
    rate = models.DecimalField(default=0, max_digits=12, decimal_places=2)
    # total amount is product of rate and quantity
    total_amount = models.DecimalField(
        blank=True, null=True, max_digits=12, decimal_places=2
    )
    tax = models.DecimalField(blank=True, null=True, max_digits=12, decimal_places=2)
    currency = models.CharField(
        max_length=3, choices=CURRENCY_CODES, blank=True, null=True
    )
    phone = models.CharField(max_length=20, null=True, blank=True)

    amount_due = models.DecimalField(
        blank=True, null=True, max_digits=12, decimal_places=2
    )
    amount_paid = models.DecimalField(
        blank=True, null=True, max_digits=12, decimal_places=2
    )
    is_email_sent = models.BooleanField(default=False)
    status = models.CharField(choices=INVOICE_STATUS, max_length=15, default="Draft")
    details = models.TextField(_("Details"), null=True, blank=True)
    due_date = models.DateField(blank=True, null=True)
    accounts = models.ManyToManyField(Account, related_name="accounts_invoices")
    teams = models.ManyToManyField(Teams, related_name="invoices_teams")

    org = models.ForeignKey(Org, on_delete=models.CASCADE, related_name="invoices")

    class Meta:
        verbose_name = "Invoice"
        verbose_name_plural = "Invoices"
        db_table = "invoice"
        ordering = ("-created_at",)

    def __str__(self):
        """Unicode representation of Invoice."""
        return f"{self.invoice_number}"

    def save(self, *args, **kwargs):
        if not self.invoice_number:
            self.invoice_number = self.invoice_id_generator()
            while Invoice.objects.filter(invoice_number=self.invoice_number).exists():
                self.invoice_number = self.invoice_id_generator(
                    prev_invoice_number=self.invoice_number
                )
        super(Invoice, self).save(*args, **kwargs)

    def invoice_id_generator(self, prev_invoice_number=None):
        if prev_invoice_number:
            prev_invoice_number += 1
            return prev_invoice_number
        date = datetime.datetime.now().strftime("%d%m%Y")
        return int(date + "0001")

    def formatted_total_amount(self):
        currency = self.currency or "USD"
        return currency + " " + str(self.total_amount)

    def formatted_rate(self):
        currency = self.currency or "USD"
        return str(self.rate) + " " + currency

    def formatted_total_quantity(self):
        return str(self.quantity) + " " + "Hours"

    def is_draft(self):
        if self.status == "Draft":
            return True
        else:
            return False

    def is_sent(self):
        if self.status == "Sent" and self.is_email_sent == False:
            return True
        else:
            return False

    def is_resent(self):
        if self.status == "Sent" and self.is_email_sent == True:
            return True
        else:
            return False

    def is_paid_or_cancelled(self):
        if self.status in ["Paid", "Cancelled"]:
            return True
        else:
            return False

    @property
    def created_on_arrow(self):
        return timesince(self.created_at) + " ago"

    @property
    def get_team_users(self):
        team_user_ids = list(self.teams.values_list("users__id", flat=True))
        return Profile.objects.filter(id__in=team_user_ids)

    @property
    def get_team_and_assigned_users(self):
        team_user_ids = list(self.teams.values_list("users__id", flat=True))
        assigned_user_ids = list(self.assigned_to.values_list("id", flat=True))
        user_ids = team_user_ids + assigned_user_ids
        return Profile.objects.filter(id__in=user_ids)

    @property
    def get_assigned_users_not_in_teams(self):
        team_user_ids = list(self.teams.values_list("users__id", flat=True))
        assigned_user_ids = list(self.assigned_to.values_list("id", flat=True))
        user_ids = set(assigned_user_ids) - set(team_user_ids)
        return Profile.objects.filter(id__in=list(user_ids))


class InvoiceHistory(BaseModel):
    """Model definition for InvoiceHistory.
    This model is used to track/keep a record of the updates made to original invoice object.
    """

    INVOICE_STATUS = (
        ("Draft", "Draft"),
        ("Sent", "Sent"),
        ("Paid", "Paid"),
        ("Pending", "Pending"),
        ("Cancelled", "Cancel"),
    )
    invoice = models.ForeignKey(
        Invoice, on_delete=models.CASCADE, related_name="invoice_history"
    )
    invoice_title = models.CharField(_("Invoice Title"), max_length=50)
    invoice_number = models.CharField(_("Invoice Number"), max_length=50)
    from_address = models.ForeignKey(
        Address,
        related_name="invoice_history_from_address",
        on_delete=models.SET_NULL,
        null=True,
    )
    to_address = models.ForeignKey(
        Address,
        related_name="invoice_history_to_address",
        on_delete=models.SET_NULL,
        null=True,
    )
    name = models.CharField(_("Name"), max_length=100)
    email = models.EmailField(_("Email"))
    assigned_to = models.ManyToManyField(
        Profile, related_name="invoice_history_assigned_to"
    )
    # quantity is the number of hours worked
    quantity = models.PositiveIntegerField(default=0)
    # rate is the rate charged
    rate = models.DecimalField(default=0, max_digits=12, decimal_places=2)
    # total amount is product of rate and quantity
    total_amount = models.DecimalField(
        blank=True, null=True, max_digits=12, decimal_places=2
    )
    currency = models.CharField(
        max_length=3, choices=CURRENCY_CODES, blank=True, null=True
    )
    phone = models.CharField(max_length=20, null=True, blank=True)
    updated_by = models.ForeignKey(
        Profile,
        related_name="invoice_history_updated_by",
        on_delete=models.SET_NULL,
        null=True,
    )

    amount_due = models.DecimalField(
        blank=True, null=True, max_digits=12, decimal_places=2
    )
    amount_paid = models.DecimalField(
        blank=True, null=True, max_digits=12, decimal_places=2
    )
    is_email_sent = models.BooleanField(default=False)
    status = models.CharField(choices=INVOICE_STATUS, max_length=15, default="Draft")
    # details or description here stores the fields changed in the original invoice object
    details = models.TextField(_("Details"), null=True, blank=True)
    due_date = models.DateField(blank=True, null=True)
    org = models.ForeignKey(
        Org,
        on_delete=models.CASCADE,
        related_name="invoice_histories",
    )

    class Meta:
        verbose_name = "InvoiceHistory"
        verbose_name_plural = "InvoiceHistories"
        db_table = "invoice_history"
        ordering = ("-created_at",)
        indexes = [
            models.Index(fields=["org", "-created_at"]),
        ]

    def __str__(self):
        """Unicode representation of Invoice."""
        return self.invoice_number

    def save(self, *args, **kwargs):
        if not self.org_id and self.invoice_id:
            self.org_id = self.invoice.org_id
        super().save(*args, **kwargs)

    def formatted_total_amount(self):
        currency = self.currency or "USD"
        return currency + " " + str(self.total_amount)

    def formatted_rate(self):
        currency = self.currency or "USD"
        return str(self.rate) + " " + currency

    def formatted_total_quantity(self):
        return str(self.quantity) + " " + "Hours"

    @property
    def created_on_arrow(self):
        return timesince(self.created_at) + " ago"


# Phase 3: Enhanced Quotes with Line Items


class Product(BaseModel):
    """Product Catalog for Line Items"""

    name = models.CharField(max_length=255)
    description = models.TextField(blank=True, null=True)
    sku = models.CharField(max_length=100, blank=True, null=True)
    price = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    currency = models.CharField(max_length=3, choices=CURRENCY_CODES, blank=True, null=True)
    category = models.CharField(max_length=100, blank=True, null=True)
    is_active = models.BooleanField(default=True)
    org = models.ForeignKey(Org, on_delete=models.CASCADE, related_name="products")

    class Meta:
        verbose_name = "Product"
        verbose_name_plural = "Products"
        db_table = "product"
        ordering = ("name",)
        unique_together = [["sku", "org"]]

    def __str__(self):
        return self.name


class InvoiceLineItem(BaseModel):
    """Line Item for Invoices"""

    invoice = models.ForeignKey(
        Invoice, on_delete=models.CASCADE, related_name="line_items"
    )
    product = models.ForeignKey(
        Product,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="invoice_line_items",
    )
    description = models.CharField(max_length=500)
    quantity = models.DecimalField(max_digits=10, decimal_places=2, default=1)
    unit_price = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    total = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    order = models.PositiveIntegerField(default=0)
    org = models.ForeignKey(
        Org,
        on_delete=models.CASCADE,
        related_name="invoice_line_items",
    )

    class Meta:
        verbose_name = "Invoice Line Item"
        verbose_name_plural = "Invoice Line Items"
        db_table = "invoice_line_item"
        ordering = ("order",)
        indexes = [
            models.Index(fields=["org", "order"]),
        ]

    def __str__(self):
        return f"{self.invoice.invoice_number} - {self.description}"

    def save(self, *args, **kwargs):
        # Auto-calculate total
        self.total = self.quantity * self.unit_price
        if not self.org_id and self.invoice_id:
            self.org_id = self.invoice.org_id
        super().save(*args, **kwargs)

    @property
    def formatted_unit_price(self):
        if self.invoice and self.invoice.currency:
            return f"{self.invoice.currency} {self.unit_price}"
        return str(self.unit_price)

    @property
    def formatted_total(self):
        if self.invoice and self.invoice.currency:
            return f"{self.invoice.currency} {self.total}"
        return str(self.total)
